基于BP神经网络对MNIST数据集检测识别(Pytorch,Tensorflow版本) 您所在的位置:网站首页 tensorflow 梯度累加 基于BP神经网络对MNIST数据集检测识别(Pytorch,Tensorflow版本)

基于BP神经网络对MNIST数据集检测识别(Pytorch,Tensorflow版本)

2023-06-13 12:24| 来源: 网络整理| 查看: 265

基于BP神经网络对MNIST数据集检测识别 1.作者介绍2.基于BP神经网络对MNIST数据集检测识别2.1 BP神经网络介绍2.2 神经元模型2.3 激活函数2.4 BP神经网络基础架构2.5 BP神经网络正向传播反向传播 3.基于BP神经网络对MNIST数据集检测识别实验3.1 MNIST数据集介绍3.2代码实现(Pytorch版本)3.3代码实现(TensorFlow版本) 4. BP神经网络注意问题4.1 代码相关问题

1.作者介绍

任陇刚,男,西安工程大学电子信息学院,2022级研究生 研究方向:机器视觉与人工智能 电子邮件:[email protected]

张思怡,女,西安工程大学电子信息学院,2022级研究生,张宏伟人工智能课题组 研究方向:机器视觉与人工智能 电子邮件:[email protected]

2.基于BP神经网络对MNIST数据集检测识别 2.1 BP神经网络介绍

首先从名称中可以看出,Bp神经网络可以分为两个部分,bp和神经网络。bp是 Back Propagation 的简写 ,意思是反向传播。BP网络能学习和存贮大量的输入-输出模式映射关系,而无需事前揭示描述这种映射关系的数学方程。它的学习规则是使用最速下降法,通过反向传播来不断调整网络的权值和阈值,使网络的误差平方和最小。其主要的特点是:信号是正向传播的,而误差是反向传播的。算法流程图如下: 在这里插入图片描述

2.2 神经元模型

每个神经元都接受来自其它神经元的输入信号,每个信号都通过一个带有权重的连接传递,神经元把这些信号加起来得到一个总输入值,然后将总输入值与神经元的阈值进行对比(模拟阈值电位),然后通过一个“激活函数”处理得到最终的输出(模拟细胞的激活),这个输出又会作为之后神经元的输入一层一层传递下去。神经元模型如下: 在这里插入图片描述

2.3 激活函数

引入激活函数的目的是在模型中引入非线性。如果没有激活函数(其实相当于激励函数是f(x) = x),那么无论你的神经网络有多少层,最终都是一个线性映射,那么网络的逼近能力就相当有限,单纯的线性映射无法解决线性不可分问题。正因为上面的原因,我们决定引入非线性函数作为激励函数,这样深层神经网络表达能力就更加强大 BP神经网络算法常用的激活函数: 1)Sigmoid(logistic),也称为S型生长曲线,函数在用于分类器时,效果更好。 在这里插入图片描述 在这里插入图片描述 2)Tanh函数(双曲正切函数),解决了logistic中心不为0的缺点,但依旧有梯度易消失的缺点。 在这里插入图片描述 在这里插入图片描述 3)relu函数是一个通用的激活函数,针对Sigmoid函数和tanh的缺点进行改进的,目前在大多数情况下使用 在这里插入图片描述 在这里插入图片描述

2.4 BP神经网络基础架构

BP网络由输入层、隐藏层、输出层组成 在这里插入图片描述 输入层:信息的输入端,是读入你输入的数据的 隐藏层:信息的处理端,可以设置这个隐藏层的层数(在这里一层隐藏层,q个神经元) 输出层:信息的输出端,也就是我们要的结果 对于上图的只含一个隐藏层的神经网络模型:BP神经网络的过程主要分为两个阶段, 第一阶段是信号的正向传播,从输入层经过隐含层,最后到达输出层; 第二阶段是误差的反向传播,从输出层到隐含层,最后到输入层,依次调节隐含层到输出层的权重和偏置,输入层到隐含层的权重和偏置

2.5 BP神经网络正向传播反向传播

正向传播过程 正向传播就是让信息从输入层进入网络,依次经过每一层的计算,得到最终输出层结果的过程 从输入层到隐藏层: 从隐藏层到输出层: 以y1举例。y1里的输出自然有来自b1,b2,…bq的。那么分别按照权重去乘。

类似的我们可以求解出y2……yn 反向传播过程 基本思想就是通过计算输出层与期望值之间的误差来调整网络参数,从而使得误差变小。 计算误差公式如下:(差值的平方) 在这里插入图片描述 如何调整权重的大小,才能使损失函数不断地变小呢? 1.梯度下降法 2.(随机梯度下降)SGD 3.(自适应优化)Adam

3.基于BP神经网络对MNIST数据集检测识别实验 3.1 MNIST数据集介绍

MNIST 数据集可在 http://yann.lecun.com/exdb/mnist/ 获取,它包含了四个部分: •Training set images: train-images-idx3-ubyte.gz (9.9 MB, 解压后 47 MB, 包含 60,000 个样本) •Training set labels: train-labels-idx1-ubyte.gz (29 KB, 解压后 60 KB, 包含 60,000 个标签) •Test set images: t10k-images-idx3-ubyte.gz (1.6 MB, 解压后 7.8 MB, 包含 10,000 个样本) •Test set labels: t10k-labels-idx1-ubyte.gz (5KB, 解压后 10 KB, 包含 10,000 个标签) Mnist数据集中的标签是介于0~9的数字,Mnist中的标签是用独热编码(one-hot-vectors)表示的,一个one-hot向量除了某一位数字是1以外,其余维度的数组都是0,比如标签0用独热编码表示为([1, 0, 0, 0, 0, 0, 0, 0, 0, 0]),标签3用独热编码表示为([0, 0, 0, 1, 0, 0, 0, 0, 0, 0])。所以,Mnist数据集中所有的标签mnist.train.labels是一个[60000, 10]的数字矩阵。 在这里插入图片描述 训练图片和测试图片: 每一张图片包含2828个像素。Mnist数据集把代表一张图片的二维数据转开成一个向量,长度为2828=784。因此在Mnist的训练数据集中mnist.train.images是一个形状为[60000, 784]的张量,第一个维度数字用来索引图片,第二个维度数字用来索引每张图片中的像素点,图片里的某个像素的强度值介于0-1之间。(图8部分数据可视化) 在这里插入图片描述 在这里插入图片描述

3.2代码实现(Pytorch版本)

定义transform对象,其定义了数据集中的图片应该做怎样的处理:

transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,)), ])

加载和下载训练测试数据集,这里使用pytorch提供的API进行下载:

train_set = datasets.MNIST('data', # 下载到该文件夹下 download=not os.path.exists('train_set'), # 是否下载,如果下载过,则不重复下载 train=True, # 是否为训练集 transform=transform # 要对图片做的transform ) print(train_set) test_set = datasets.MNIST('data', download=not os.path.exists('test_set'), train=False, transform=transform ) print(test_set)

构建训练数据集和测试数据集的DataLoader对象:

train_loader = torch.utils.data.DataLoader(train_set, batch_size=128, shuffle=True) test_loader = torch.utils.data.DataLoader(test_set, batch_size=128, shuffle=True) dataiter = iter(train_loader) images, labels = dataiter.next() print(images.shape) print(labels.shape)

在上面,batch_size=64,每个batch送进64张图片每个图片只有一个通道(灰度图),大小为28x28。抽一张绘制一下:

plt.imshow(images[0].numpy().squeeze(), cmap='gray_r');

定义神经网络:

class NerualNetwork(nn.Module): def __init__(self): super().__init__() """ 定义第一个线性层, 输入为图片(28x28), 输出为第一个隐层的输入,大小为128。 """ self.linear1 = nn.Linear(28 * 28, 128) # 在第一个隐层使用ReLU激活函数 self.relu1 = nn.ReLU() """ 定义第二个线性层, 输入是第一个隐层的输出, 输出为第二个隐层的输入,大小为64。 """ self.linear2 = nn.Linear(128, 64) # 在第二个隐层使用ReLU激活函数 self.relu2 = nn.ReLU() """ 定义第三个线性层, 输入是第二个隐层的输出, 输出为输出层,大小为10 """ self.linear3 = nn.Linear(64, 10) # 最终的输出经过softmax进行归一化 self.softmax = nn.LogSoftmax(dim=1) # 上述操作可以直接使用nn.Sequential写成如下形式: self.model = nn.Sequential(nn.Linear(28 * 28, 128), nn.ReLU(), nn.Linear(128, 64), nn.ReLU(), nn.Linear(64, 10), nn.LogSoftmax(dim=1) )

网络前向传播

def forward(self, x): """ 定义神经网络的前向传播 x: 图片数据, shape为(64, 1, 28, 28) """ # 首先将x的shape转为(64, 784) x = x.view(x.shape[0], -1) # 接下来进行前向传播 x = self.linear1(x) x = self.relu1(x) x = self.linear2(x) x = self.relu2(x) x = self.linear3(x) x = self.softmax(x) # 上述一串,可以直接使用 x = self.model(x) 代替。 return x

模型实例化: 定义损失函数,这里选用负对数似然损失函数(NLLLoss, negative log likelihood loss),其常用于分类任务 定义优化器,这里使用随机梯度下降法,学习率设置为0.003,momentum取默认的0.9(用于防止过拟合)

model = NerualNetwork() criterion = nn.NLLLoss() optimizer = optim.SGD(model.parameters(), lr=0.003, momentum=0.9)

模型训练过程:

time0 = time() # 记录下当前时间 epochs = 15 # 一共训练15轮 for e in range(epochs): running_loss = 0 # 本轮的损失值 for images, labels in train_loader: # 前向传播获取预测值 output = model(images) # 计算损失 loss = criterion(output, labels) # 进行反向传播 loss.backward() # 更新权重 optimizer.step() # 清空梯度 optimizer.zero_grad() # 累加损失 running_loss += loss.item() else: # 一轮循环结束后打印本轮的损失函数 print("Epoch {} - Training loss: {}".format(e+1, running_loss / len(train_loader))) # 打印总的训练时间 print("\nTraining Time (in minutes) =", (time() - time0) / 60) correct_count, all_count = 0, 0 model.eval() # 将模型设置为评估模式 # 从test_loader中一批一批加载图片 for images, labels in test_loader: # 循环检测这一批图片 for i in range(len(labels)): logps = model(images[i]) # 进行前向传播,获取预测值 probab = list(logps.detach().numpy()[0]) # 将预测结果转为概率列表。[0]是取第一张照片的10个数字的概率列表(因为一次只预测一张照片) pred_label = probab.index(max(probab)) # 取最大的index作为预测结果 true_label = labels.numpy()[i] if (true_label == pred_label): # 判断是否预测正确 correct_count += 1 all_count += 1 print("Number Of Images Tested =", all_count) print("\nModel Accuracy ={}%".format((correct_count / all_count)*100))

完整代码:

import os import numpy as np import torch import torchvision import matplotlib.pyplot as plt from time import time from torchvision import datasets, transforms from torch import nn, optim transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,)), ]) train_set = datasets.MNIST('data', # 下载到该文件夹下 download=not os.path.exists('train_set'), # 是否下载,如果下载过,则不重复下载 train=True, # 是否为训练集 transform=transform # 要对图片做的transform ) print(train_set) test_set = datasets.MNIST('data', download=not os.path.exists('test_set'), train=False, transform=transform ) print(test_set) train_loader = torch.utils.data.DataLoader(train_set, batch_size=128, shuffle=True) test_loader = torch.utils.data.DataLoader(test_set, batch_size=128, shuffle=True) dataiter = iter(train_loader) images, labels = dataiter.next() print(images.shape) print(labels.shape) plt.imshow(images[0].numpy().squeeze(), cmap='gray_r'); class NerualNetwork(nn.Module): def __init__(self): super().__init__() """ 定义第一个线性层, 输入为图片(28x28), 输出为第一个隐层的输入,大小为128。 """ self.linear1 = nn.Linear(28 * 28, 128) # 在第一个隐层使用ReLU激活函数 self.relu1 = nn.ReLU() """ 定义第二个线性层, 输入是第一个隐层的输出, 输出为第二个隐层的输入,大小为64。 """ self.linear2 = nn.Linear(128, 64) # 在第二个隐层使用ReLU激活函数 self.relu2 = nn.ReLU() """ 定义第三个线性层, 输入是第二个隐层的输出, 输出为输出层,大小为10 """ self.linear3 = nn.Linear(64, 10) # 最终的输出经过softmax进行归一化 self.softmax = nn.LogSoftmax(dim=1) # 上述操作可以直接使用nn.Sequential写成如下形式: self.model = nn.Sequential(nn.Linear(28 * 28, 128), nn.ReLU(), nn.Linear(128, 64), nn.ReLU(), nn.Linear(64, 10), nn.LogSoftmax(dim=1) ) def forward(self, x): """ 定义神经网络的前向传播 x: 图片数据, shape为(64, 1, 28, 28) """ # 首先将x的shape转为(64, 784) x = x.view(x.shape[0], -1) # 接下来进行前向传播 x = self.linear1(x) x = self.relu1(x) x = self.linear2(x) x = self.relu2(x) x = self.linear3(x) x = self.softmax(x) # 上述一串,可以直接使用 x = self.model(x) 代替。 return x model = NerualNetwork() criterion = nn.NLLLoss() optimizer = optim.SGD(model.parameters(), lr=0.003, momentum=0.9) time0 = time() # 记录下当前时间 epochs = 15 # 一共训练15轮 for e in range(epochs): running_loss = 0 # 本轮的损失值 for images, labels in train_loader: # 前向传播获取预测值 output = model(images) # 计算损失 loss = criterion(output, labels) # 进行反向传播 loss.backward() # 更新权重 optimizer.step() # 清空梯度 optimizer.zero_grad() # 累加损失 running_loss += loss.item() else: # 一轮循环结束后打印本轮的损失函数 print("Epoch {} - Training loss: {}".format(e+1, running_loss / len(train_loader))) # 打印总的训练时间 print("\nTraining Time (in minutes) =", (time() - time0) / 60) correct_count, all_count = 0, 0 model.eval() # 将模型设置为评估模式 # 从test_loader中一批一批加载图片 for images, labels in test_loader: # 循环检测这一批图片 for i in range(len(labels)): logps = model(images[i]) # 进行前向传播,获取预测值 probab = list(logps.detach().numpy()[0]) # 将预测结果转为概率列表。[0]是取第一张照片的10个数字的概率列表(因为一次只预测一张照片) pred_label = probab.index(max(probab)) # 取最大的index作为预测结果 true_label = labels.numpy()[i] if (true_label == pred_label): # 判断是否预测正确 correct_count += 1 all_count += 1 print("Number Of Images Tested =", all_count) print("\nModel Accuracy ={}%".format((correct_count / all_count)*100)) 3.3代码实现(TensorFlow版本)

加载数据

#加载数据 mnist = tf.keras.datasets.mnist (train_x,train_y),(test_x,test_y) = mnist.load_data() print('\n train_x:%s, train_y:%s, test_x:%s, test_y:%s'%(train_x.shape,train_y.shape,test_x.shape,test_y.shape))

数据预处理

#归一化、并转换为tensor张量,数据类型为float32. X_train,X_test = tf.cast(train_x/255.0,tf.float32),tf.cast(test_x/255.0,tf.float32) y_train,y_test = tf.cast(train_y,tf.int16),tf.cast(test_y,tf.int16)

建立模型(单层隐含层的神经网络)

#建立模型 model = tf.keras.Sequential() model.add(tf.keras.layers.Flatten(input_shape=(28,28))) #添加Flatten层说明输入数据的形状 model.add(tf.keras.layers.Dense(128,activation='relu')) #添加隐含层,为全连接层,128个节点,relu激活函数 model.add(tf.keras.layers.Dense(10,activation='softmax')) #添加输出层,为全连接层,10个节点,softmax激活函数 print('\n',model.summary()) #查看网络结构和参数信息

配置模型训练方法

#配置模型训练方法 #adam算法参数采用keras默认的公开参数,损失函数采用稀疏交叉熵损失函数,准确率采用稀疏分类准确率函数 model.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=['sparse_categorical_accuracy'])

评估模型

#评估模型 model.evaluate(X_test,y_test,verbose=2) #每次迭代输出一条记录,来评价该模型是否有比较好的泛化能力

保存模型

#保存整个模型 model.save('mnist_weights.h5')

结果可视化

#结果可视化 print(history.history) loss = history.history['loss'] #训练集损失 val_loss = history.history['val_loss'] #测试集损失 acc = history.history['sparse_categorical_accuracy'] #训练集准确率 val_acc = history.history['val_sparse_categorical_accuracy'] #测试集准确率

运行结果 在这里插入图片描述 测试结果 在这里插入图片描述 在这里插入图片描述 完整代码:

########手写数字数据集########## ###########保存模型############ ########1层隐含层(全连接层)########## #60000条训练数据和10000条测试数据,28x28像素的灰度图像 #隐含层激活函数:ReLU函数 #输出层激活函数:softmax函数(实现多分类) #损失函数:稀疏交叉熵损失函数 #输入层有784个节点,隐含层有128个神经元,输出层有10个节点 import tensorflow as tf import matplotlib.pyplot as plt import numpy as np import time print('--------------') nowtime = time.strftime('%Y-%m-%d %H:%M:%S') print(nowtime) #指定GPU import os os.environ["CUDA_VISIBLE_DEVICES"] = "0" # gpus = tf.config.experimental.list_physical_devices('GPU') # tf.config.experimental.set_memory_growth(gpus[0],True) #初始化 plt.rcParams['font.sans-serif'] = ['SimHei'] #加载数据 mnist = tf.keras.datasets.mnist (train_x,train_y),(test_x,test_y) = mnist.load_data() print('\n train_x:%s, train_y:%s, test_x:%s, test_y:%s'%(train_x.shape,train_y.shape,test_x.shape,test_y.shape)) #数据预处理 #X_train = train_x.reshape((60000,28*28)) #Y_train = train_y.reshape((60000,28*28)) #后面采用tf.keras.layers.Flatten()改变数组形状 #归一化、并转换为tensor张量,数据类型为float32. X_train,X_test = tf.cast(train_x/255.0,tf.float32),tf.cast(test_x/255.0,tf.float32) y_train,y_test = tf.cast(train_y,tf.int16),tf.cast(test_y,tf.int16) #建立模型 model = tf.keras.Sequential() model.add(tf.keras.layers.Flatten(input_shape=(28,28))) #添加Flatten层说明输入数据的形状 model.add(tf.keras.layers.Dense(128,activation='relu')) #添加隐含层,为全连接层,128个节点,relu激活函数 model.add(tf.keras.layers.Dense(10,activation='softmax')) #添加输出层,为全连接层,10个节点,softmax激活函数 print('\n',model.summary()) #查看网络结构和参数信息 #配置模型训练方法 #adam算法参数采用keras默认的公开参数,损失函数采用稀疏交叉熵损失函数,准确率采用稀疏分类准确率函数 model.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=['sparse_categorical_accuracy']) #训练模型 #批量训练大小为64,迭代5次,测试集比例0.2(48000条训练集数据,12000条测试集数据) print('--------------') nowtime = time.strftime('%Y-%m-%d %H:%M:%S') print('训练前时刻:'+str(nowtime)) history = model.fit(X_train,y_train,batch_size=64,epochs=30,validation_split=0.2) print('--------------') nowtime = time.strftime('%Y-%m-%d %H:%M:%S') print('训练后时刻:'+str(nowtime)) #评估模型 model.evaluate(X_test,y_test,verbose=2) #每次迭代输出一条记录,来评价该模型是否有比较好的泛化能力 #保存模型参数 #model.save_weights('C:\\Users\\xuyansong\\Desktop\\深度学习\\python\\MNIST\\模型参数\\mnist_weights.h5') #保存整个模型 model.save('mnist_weights.h5') #结果可视化 print(history.history) loss = history.history['loss'] #训练集损失 val_loss = history.history['val_loss'] #测试集损失 acc = history.history['sparse_categorical_accuracy'] #训练集准确率 val_acc = history.history['val_sparse_categorical_accuracy'] #测试集准确率 plt.figure(figsize=(10,3)) plt.subplot(121) plt.plot(loss,color='b',label='train') plt.plot(val_loss,color='r',label='test') plt.ylabel('loss') plt.legend() plt.subplot(122) plt.plot(acc,color='b',label='train') plt.plot(val_acc,color='r',label='test') plt.ylabel('Accuracy') plt.legend() #暂停5秒关闭画布,否则画布一直打开的同时,会持续占用GPU内存 #根据需要自行选择 #plt.ion() #打开交互式操作模式 #plt.show() #plt.pause(5) #plt.close() #使用模型 plt.figure() for i in range(10): num = np.random.randint(1,10000) plt.subplot(2,5,i+1) plt.axis('off') plt.imshow(test_x[num],cmap='gray') demo = tf.reshape(X_test[num],(1,28,28)) y_pred = np.argmax(model.predict(demo)) plt.title('标签值:'+str(test_y[num])+'\n预测值:'+str(y_pred)) y_pred = np.argmax(model.predict(X_test[0:5]),axis=1) print('X_test[0:5]: %s'%(X_test[0:5].shape)) print('y_pred: %s'%(y_pred)) plt.ion() #打开交互式操作模式 plt.show() plt.pause(5) plt.close() 4. BP神经网络注意问题

a)参数选取很重要。 用最基本的BP 算法来训练BP 神经网络时,学习率、均方误差、权值、阈值的设置都对网络的训练均有影响。综合选取合理的值,将有利于网络的训练。在最基本的BP 算法中,学习率在整个训练过程是保持不变的,学习率过大,算法可能振荡而不稳定;学习率过小,则收敛速度慢,训练时间长。 b)存在麻痹现象。由于优化的目标函数很复杂,它必然会在神经元输出接近0或1的情况下,出现一些平坦区,在这些区域内,权值误差改变很小,使训练过程几乎停顿; c) 搜索步长提前确定。 为了使网络执行BP算法,不能用传统的一维搜索法求每次迭代的步长,而必须把步长的更新规则预先赋予网络,这种方法将引起算法低效。 不同epoch下的Accurary可视化结果:

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

4.1 代码相关问题

Dataloader中num_worker设置 在这里插入图片描述 在这里插入图片描述 •num_workers=0表示只有主进程去加载batch数据,这个可能会是一个瓶颈。 •num_workers = 1表示只有一个worker进程用来加载batch数据,而主进程是不参与数据加载的。这样速度也会很慢。 •num_workers>0 表示只有指定数量的worker进程去加载数据,主进程不参与。增加num_works也同时会增加cpu内存的消耗。所以num_workers的值依赖于 batch size和机器性能。 •一般开始是将num_workers设置为等于计算机上的CPU数量 •最好的办法是缓慢增加num_workers,直到训练速度不再提高,就停止增加num_workers的值。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有